/*
 * Created on Feb 13, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell;

import java.util.Collection;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

/**
 * @author dfhuynh
 */
public class Cache {
	final static private Logger s_logger = Logger.getLogger(Cache.class);
	
	final static protected Set			s_allCaches = new HashSet();
	
	final protected int		m_size;
	protected List			m_history;
	protected Map			m_keyToObject;
	
	final protected String		m_ownerClassName;
	final protected String		m_name;
	
	protected long m_hits;
	protected long m_misses;
	
	public Cache(int size, Class ownerClass, String name) {
		m_size = size;
		m_history = new ArrayList();
		m_keyToObject = new HashMap(m_size * 2);
		
		m_ownerClassName = ownerClass.getName();
		m_name = name;
		
		s_allCaches.add(this);
	}
	
	public Object get(Object key) {
		Object obj = m_keyToObject.get(key);
		if (obj != null) {
			updateHistory(key);
			cacheHit();
		} else {
			cacheMiss();
		}
		return obj;
	}
	
	public void put(Object key, Object obj) {
		m_keyToObject.put(key, obj);
		updateHistory(key);
	}
	
	public Object remove(Object key) {
		return m_keyToObject.remove(key);
	}
	
	protected void updateHistory(Object key) {
		if (m_history.size() == 0) {
			m_history.add(key);
		} else {
			Object key2 = m_history.get(m_history.size() - 1);
			if (key == null ? key2 != null : !key.equals(key2)) {
				m_history.add(key);
				if (m_history.size() > 2 * m_size) {
					flushCache();
				}
			}
		}
	}
	
	protected void flushCache() {
		int max = m_history.size();
		int start = max - m_size / 2;
		
		Map newKeyToObject = new HashMap(m_size * 2);
		
		for (int i = start; i < max; i++) {
			Object key = m_history.get(i);
			Object value = m_keyToObject.get(key);
			
			newKeyToObject.put(key, value);
		}
		
		m_history = new ArrayList(m_history.subList(start, max));
		m_keyToObject = newKeyToObject;
	}
	
	public void clear() {
		m_history.clear();
		m_keyToObject.clear();
	}
	
	public Collection values() {
		return m_keyToObject.values();
	}
	
	public Set keySet() {
		return m_keyToObject.keySet();
	}
	
	protected void cacheMiss() {
		m_misses++;
	}
	
	protected void cacheHit() {
		m_hits++;
		//System.out.println("missed " + m_ownerClassName + " " + m_name);
	}
	
	protected void printInfo() {
		System.out.println(m_ownerClassName + ": " + m_name + ": " + 
				m_misses + " " + m_hits + " " +
				(m_hits / (m_hits + (double) m_misses)));
	}
	
	static public void printSummary() {
		Iterator i = s_allCaches.iterator();
		while (i.hasNext()) {
			Cache cache = (Cache) i.next();
			
			cache.printInfo();
		}
	}
}
